package com.hanxiaozhang.juc.no1completionservice;

import java.util.concurrent.*;

/**
 * 〈一句话功能简述〉<br>
 * 〈CompletionService接口的使用教程〉
 * <p>
 * CompletionService：
 * - 可以使得先完成的任务先被取出，减少了不必要的等待时间。
 * - 底层实现了一个任务完成的队列，并且可以按照任务完成的顺序来获取任务的结果。
 * <p>
 * ExecutorCompletionService实现类：
 * CompletionService的唯一实现，内部数据结构加了一个BlockingQueue来保存已经完成的Future对象。
 * 只有当这个Future对象状态是结束时，才会加入到这个Queue中。take()其实就是Producer-Consumer模型中的Consumer。
 * 它会从Queue中取出Future对象，如果Queue是空的，就会阻塞在那里，直到有完成的Future对象加入到Queue中。
 * <p>
 * ExecutorCompletionService三个关键步骤：
 * 设置一个线程池、submit提交任务、take获取完成的Future。
 * <p>
 * 使用场景：
 * 1. 批量下载文件：
 * 假设你要从多个网站上下载大量的文件，可以将每个下载任务提交给CompletionService，
 * 它将负责并发地下载文件并返回下载结果。这样可以提高下载的效率，并且可以在任何一个文件下载完成后立即处理它。
 * 2. 多个商品价格查询：
 * 假设你想要比较多个不同网站上的商品价格，可以将每个商品的价格查询任务提交给CompletionService。
 * 它可以并发地查询每个网站上的商品价格，并将结果返回给你。这样可以快速地获取多个网站上的价格信息。
 * 3. 并发处理多个API请求：
 * 如果你需要同时向多个API发送请求，并等待它们的响应，可以使用CompletionService。
 * 你可以将每个请求任务提交给CompletionService，它将负责并发地发送请求并返回响应结果。
 * 这样可以提高API请求的效率，并在任意一个请求完成后立即处理它。
 *
 * @author hanxinghua
 * @create 2024/4/30
 * @since 1.0.0
 */
public class No1CompletionServiceUse {


    /**
     * 线程池大小
     */
    public static final int POOL_SIZE = 100;

    /**
     * 任务个数
     */
    public static final int TOTAL_TASK = 50;

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
        CompletionService<String> cService = new ExecutorCompletionService<>(pool);

        // 向里面扔任务
        for (int i = 0; i < TOTAL_TASK; i++) {
            // 这边不是直接通过线程池来提交任务，而是通过CompletionService来提交
            cService.submit(new WorkTask("ExecTask" + i));
        }

        // 检查线程池任务执行结果
        for (int i = 0; i < TOTAL_TASK; i++) {
            // 同样也是从CompletionService来取出结果，因为CompletionService内部实现通过一个BlockingQueue来保存已经完成的结果。
            String name = cService.take().get();
            System.out.println(name);
        }

        pool.shutdown();
    }

    /**
     * 任务
     */
    private static class WorkTask implements Callable<String> {

        private String name;

        public WorkTask(String name) {
            this.name = name;
        }

        @Override
        public String call() throws Exception {

            int sleep = (int) (Math.random() * 100);
            Thread.sleep(sleep);
            return name + "-" + sleep;
        }
    }
}
